﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace RayTrace
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        
        private void btnShow_Click(object sender, EventArgs e)
        {
            int nx = 2000; //宽度
            int ny = 2000; //高度
            int ns = 10;
            Bitmap bitmap = new Bitmap(nx, ny);
            Random rd = new Random();
            //Hitable world = new Hitable();
            //world = RandomScene();

            Point3D lookfrom = new Point3D(3, 3, 3);
            Point3D lookat = new Point3D(0, 0, -1);
            double distToFoucs = (lookfrom - lookat).Length();
            double aperture = 2.0;
            Camera cam = new Camera(lookfrom
                , lookat
                , new Vector3D(0, 1, 0)
                ,50
                , (double)(nx) / (double)(ny)
                ,aperture
                ,distToFoucs);
            List<Hitable> list = new List<Hitable>();
            list.Add(new Sphere(new Point3D(0, 0, -1), 0.5, new Lambertian(new Vector3D(0.1, 0.2, 0.5))));
            list.Add(new Sphere(new Point3D(0, -100.5, -1), 100, new Lambertian(new Vector3D(0.8, 0.8, 0.0))));
            list.Add(new Sphere(new Point3D(1, 0, -1), 0.5, new Metal(new Vector3D(0.8, 0.6, 0.2), 0.0)));
            list.Add(new Sphere(new Point3D(-1, 0, -1), 0.5, new Dielectrics(1.5)));
            list.Add(new Sphere(new Point3D(-1, 0, -1), -0.45, new Dielectrics(1.5)));
            HitableList world = new HitableList(list, 5);

            for (int i = 0; i < nx; i++)
            {
                for (int j = 0; j < ny; j++)
                {
                    Color3D col = new Color3D();
                    for (int s = 0; s < ns; s++)
                    {
                        double u = (double)(i + rd.NextDouble()) / (double)nx;
                        double v = (double)(j + rd.NextDouble()) / (double)ny;
                        Ray r = cam.GetRay(u, v);
                        Color3D colTemp = Color(r, world, 0);
                        col.R += colTemp.R;
                        col.G += colTemp.G;
                        col.B += colTemp.B;
                    }
                    col.R /= ns;
                    col.G /= ns;
                    col.B /= ns;
                    col = new Color3D(Math.Sqrt(col.R), Math.Sqrt(col.G), Math.Sqrt(col.B));
                    bitmap.SetPixel(i, ny - j - 1, col.ToRGB255Color());
                }
            }
            pictureBox1.BackgroundImage = bitmap;
            Bitmap = bitmap;
        }

        public static Color3D Color(Ray r, Hitable world, int depth)
        {
            HitRecord rec = new HitRecord();
            if (world.Hit(r, 0.001, double.MaxValue, ref rec))
            {
                Ray scattered = new Ray();
                Vector3D attenuation = new Vector3D();
                if (depth < 50 && rec.Material.Scatter(r, rec, ref
               attenuation, ref scattered))
                {
                    Color3D colorTemp = Color(scattered, world, depth + 1);
                    colorTemp.R *= attenuation.X;
                    colorTemp.G *= attenuation.Y;
                    colorTemp.B *= attenuation.Z;
                    return colorTemp;
                }
                else
                {
                    return new Color3D(0, 0, 0);
                }
            }
            else
            {
                Vector3D unitDirection = Vector3D.UnitVector(r.Direction);
                double t = 0.5 * (unitDirection.Y + 1.0);
                return new Color3D((1.0 - t) + t * 0.5, (1.0 - t) + t * 0.7,
               (1.0 - t) + t * 1);
            }
        }

        //随机数
        private static Random rd = new Random();
        
        public static Random Rd { get => rd; set => rd = value; }

        public static Vector3D RandomInUnitSphere()
        {
            Vector3D p = new Vector3D();
            do
            {
                p = 2.0 * new Vector3D(Rd.NextDouble(), Rd.NextDouble(), Rd.NextDouble()) - new Point3D(1, 1, 1);
            } while (p.SquaredLength() >= 1.0);
            return p;
        }
        public static Vector3D RandomInUnitDisk()
        {
            Vector3D p = new Vector3D();
            do
            {
                p = 2.0 * new Vector3D(Rd.NextDouble(), Rd.NextDouble(), 0) - new Point3D(1, 1, 0);
            } while (Vector3D.DotProduct(p, p) >= 1.0);
            return p;
        }  
        //折射
        public static Vector3D Reflect(Vector3D v, Vector3D n)
        {
            return v - 2 * Vector3D.DotProduct(v, n) * n;
        }
        //反射
        public static bool Refract(Vector3D v, Vector3D n, double niOverNt, ref Vector3D refracted)
        {
            Vector3D uv = Vector3D.UnitVector(v);
            double dt = Vector3D.DotProduct(uv, n);
            double discriminant = 1.0 - niOverNt * niOverNt * (1 - dt * dt);
            if (discriminant > 0)
            {
                refracted = niOverNt * (uv - n * dt) - n * Math.Sqrt(discriminant);
                return true;
            }
            else
                return false;
        }
        //全反射
        public static double Schlick(double cosine, double refIdx)
        {
            double r0 = (1 - refIdx) / (1 + refIdx);
            r0 = r0 * r0;
            return r0 + (1 - r0) * Math.Pow((1 - cosine), 5);
        }
        //添加随机球体
        public static Hitable RandomScene()
        {
            List<Hitable> list = new List<Hitable>();
            list.Add(new Sphere(new Vector3D(0, -1000, 0), 1000, new
           Lambertian(new Vector3D(0.5, 0.5, 0.5))));
            for (int a = -11; a < 11; a++)
            {
                for (int b = -11; b < 11; b++)
                {
                    double chooseMat = rd.NextDouble();
                    Vector3D center = new Vector3D(a + 0.9 * rd.NextDouble(),
                   0.2, b + 0.9 * rd.NextDouble());
                    if ((center - new Vector3D(4, 0.2, 0)).Length() > 0.9)
                    {
                        if (chooseMat < 0.8)
                        {
                            list.Add(new Sphere(center, 0.2, new
                           Lambertian(new Vector3D(rd.NextDouble() * rd.NextDouble(),
                           rd.NextDouble() * rd.NextDouble(), rd.NextDouble() *
                           rd.NextDouble()))));
                        }
                        else if (chooseMat < 0.95)
                        {
                            list.Add(new Sphere(center, 0.2, new Metal(new
                           Vector3D(0.5 * (1 + rd.NextDouble()), 0.5 * (1 + rd.NextDouble()), 0.5
                           * (1 + rd.NextDouble())), 0.5 * rd.NextDouble())));
                        }
                        else
                        {
                            list.Add(new Sphere(center, 0.2, new
                           Dielectrics(1.5)));
                        }
                    }
                }
            }
            list.Add(new Sphere(new Vector3D(0, 1, 0), 1.0, new Dielectrics(1.5)));
            list.Add(new Sphere(new Vector3D(-4, 1, 0), 1.0, new Lambertian(new Vector3D(0.4, 0.2, 0.1))));
            list.Add(new Sphere(new Vector3D(4, 1, 0), 1.0, new Metal(new Vector3D(0.7, 0.6, 0.5), 0)));
            HitableList lists = new HitableList(list, list.Count);
            return lists;
        }

        Bitmap Bitmap;
        private void btnSave_Click(object sender, EventArgs e)
        {
            Bitmap.Save("show.bmp");
        }
    }
}
